package math;

public class vec2
{
    
    public static final vec2 ZERO = new vec2();
    public static final vec2 HALF = new vec2(0.5);
    public static final vec2 ONE = new vec2(1);
    
    public static final vec2 AXIS_XP = new vec2(1, 0);
    public static final vec2 AXIS_YP = new vec2(0, 1);
    
    public static final vec2 AXIS_XN = new vec2(-1,  0);
    public static final vec2 AXIS_YN = new vec2( 0, -1);
    
    
    public double x;
    
    public double y;
    
    
    public vec2()
    {
        this.set(0, 0);
    }
    
    public vec2(double aXY)
    {
        this.set(aXY);
    }
    
    public vec2(
            double aX, 
            double aY)
    {
        this.set(aX, aY);
    }
    
    public vec2(vec2i aXYi)
    {
        this.set(aXYi);
    }
    
    
    public final void set(double aXY)
    {
        this.set(aXY, aXY);
    }
    
    public final void set(
            double aX, 
            double aY)
    {
        this.x = aX;
        this.y = aY;
    }
    
    public final void set(vec2 aXY)
    {
        this.set(aXY.x, aXY.y);
    }
    
    public final void set(vec2i aXYi)
    {
        this.set(aXYi.x, aXYi.y);
    }
    
    
    public void add(vec2 aTransl)
    {
        this.x += aTransl.x;
        this.y += aTransl.y;
    }
    
    public void add(double aTransl)
    {
        this.x += aTransl;
        this.y += aTransl;
    }
    
    public void sub(vec2 aTransl)
    {
        this.x -= aTransl.x;
        this.y -= aTransl.y;
    }
    
    public void sub(double aTransl)
    {
        this.x -= aTransl;
        this.y -= aTransl;
    }
    
    public void mul(vec2 aScale)
    {
        this.x *= aScale.x;
        this.y *= aScale.y;
    }
    
    public void mul(double aScale)
    {
        this.x *= aScale;
        this.y *= aScale;
    }
    
    public void div(vec2 aScale)
    {
        this.x /= aScale.x;
        this.y /= aScale.y;
    }
    
    public void div(double aScale)
    {
        double rScale = 1.0 / aScale;
        this.mul(rScale);
    }
    
    public void addmul(vec2 aFac0, vec2 aFac1)
    {
        this.x += aFac0.x * aFac1.x;
        this.y += aFac0.y * aFac1.y;
    }
    
    public void addmul(vec2 aFac0, double aFac1)
    {
        this.x += aFac0.x * aFac1;
        this.y += aFac0.y * aFac1;
    }
    
    public void addmul(double aFac0, vec2 aFac1)
    {
        this.addmul(aFac1, aFac0);
    }
    
    public void pow(vec2 aExp)
    {
        this.x = math.powd(this.x, aExp.x);
        this.y = math.powd(this.y, aExp.y);
    }
    
    public void pow(double aExp)
    {
        this.x = math.powd(this.x, aExp);
        this.y = math.powd(this.y, aExp);
    }
    
    public void abs()
    {
        this.x = math.abs(this.x);
        this.y = math.abs(this.y);
    }
    
    public void clamp(vec2 aBoundsLo, vec2 aBoundsHi)
    {
        this.x = math.clampd(this.x, aBoundsLo.x, aBoundsHi.x);
        this.y = math.clampd(this.y, aBoundsLo.y, aBoundsHi.y);
    }
    
    public void clamp(double aBoundsLo, double aBoundsHi)
    {
        this.x = math.clampd(this.x, aBoundsLo, aBoundsHi);
        this.y = math.clampd(this.y, aBoundsLo, aBoundsHi);
    }
    
    public void norm()
    {
        this.mul(this.rlength());
    }
    
    public void lerp(vec2 aOther, double aInterpTerm)
    {
        this.x = math.lerp(this.x, aOther.x, aInterpTerm);
        this.y = math.lerp(this.y, aOther.y, aInterpTerm);
    }
    
    public void proj(vec2 aBasis)
    {
        double projFrac = this.projFraction(aBasis);
        this.set(aBasis);
        this.mul(projFrac);
    }
    
    
    public boolean close(vec2 aOther, double aError)
    {
        return (
            math.abs(this.x - aOther.x) < aError && 
            math.abs(this.y - aOther.y) < aError
        );
    }
    
    public boolean lessAll(vec2 aOther)
    {
        return (
            this.x < aOther.x && 
            this.y < aOther.y
        );
    }
    
    public boolean lessAny(vec2 aOther)
    {
        return (
            this.x < aOther.x || 
            this.y < aOther.y
        );
    }
    
    public boolean lequalAll(vec2 aOther)
    {
        return (
            this.x <= aOther.x && 
            this.y <= aOther.y
        );
    }
    
    public boolean lequalAny(vec2 aOther)
    {
        return (
            this.x <= aOther.x || 
            this.y <= aOther.y
        );
    }
    
    public boolean greaterAll(vec2 aOther)
    {
        return (
            this.x > aOther.x && 
            this.y > aOther.y
        );
    }
    
    public boolean greaterAny(vec2 aOther)
    {
        return (
            this.x > aOther.x || 
            this.y > aOther.y
        );
    }
    
    public boolean gequalAll(vec2 aOther)
    {
        return (
            this.x >= aOther.x && 
            this.y >= aOther.y
        );
    }
    
    public boolean gequalAny(vec2 aOther)
    {
        return (
            this.x >= aOther.x || 
            this.y >= aOther.y
        );
    }
    
    
    public double lengthSq()
    {
        return lengthSq(this);
        
    }
    
    public double length()
    {
        return length(this);
    }
    
    public double rlength()
    {
        return rlength(this);
    }
    
    public double projComponent(vec2 aBasis)
    {
        return dot(this, aBasis) * aBasis.rlength();
    }
    
    public double projFraction(vec2 aBasis)
    {
        return dot(this, aBasis) / dot(aBasis, aBasis);
    }
    
    
    public vec2 copy()
    {
        vec2 copy = new vec2();
        copy.set(this);
        return copy;
    }
    
    
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("( ");
        sb.append(String.format("%.2f", this.x));
        sb.append(", ");
        sb.append(String.format("%.2f", this.y));
        sb.append(" )");
        return sb.toString();
    }
    
    
    
    public static double dot(vec2 aA, vec2 aB)
    {
        return (
            aA.x * aB.x + 
            aA.y * aB.y
        );
    }
    
    public static double rlength(vec2 aA)
    {
        return math.rsqrt(dot(aA, aA));
    }
    
    public static double length(vec2 aA)
    {
        return math.sqrt(dot(aA, aA));
    }
    
    public static double lengthSq(vec2 aA)
    {
        return dot(aA, aA);
    }
    
    public static double distance(vec2 aA, vec2 aB)
    {
        return length(sub(aA, aB));
    }
    
    public static double distanceSq(vec2 aA, vec2 aB)
    {
        return lengthSq(sub(aA, aB));
    }
    
    
    public static vec2 add(vec2 aA, vec2 aB)
    {
        vec2 add = aA.copy();
        add.add(aB);
        return add;
    }
    
    public static vec2 add(vec2 aA, double aB)
    {
        vec2 add = aA.copy();
        add.add(aB);
        return add;
    }
    
    public static vec2 add(double aA, vec2 aB)
    {
        vec2 add = new vec2(aA);
        add.add(aB);
        return add;
    }
    
    public static vec2 add(double aA, double aB)
    {
        double add = aA + aB;
        return new vec2(add);
    }
    
    public static vec2 sub(vec2 aA, vec2 aB)
    {
        vec2 sub = aA.copy();
        sub.sub(aB);
        return sub;
    }
    
    public static vec2 sub(vec2 aA, double aB)
    {
        vec2 sub = aA.copy();
        sub.sub(aB);
        return sub;
    }
    
    public static vec2 sub(double aA, vec2 aB)
    {
        vec2 sub = new vec2(aA);
        sub.sub(aB);
        return sub;
    }
    
    public static vec2 sub(double aA, double aB)
    {
        double sub = aA - aB;
        return new vec2(sub);
    }
    
    public static vec2 mul(vec2 aA, vec2 aB)
    {
        vec2 mul = aA.copy();
        mul.mul(aB);
        return mul;
    }
    
    public static vec2 mul(vec2 aA, double aB)
    {
        vec2 mul = aA.copy();
        mul.mul(aB);
        return mul;
    }
    
    public static vec2 mul(double aA, vec2 aB)
    {
        vec2 mul = new vec2(aA);
        mul.mul(aB);
        return mul;
    }
    
    public static vec2 mul(double aA, double aB)
    {
        double mul = aA * aB;
        return new vec2(mul);
    }
    
    public static vec2 div(vec2 aA, vec2 aB)
    {
        vec2 div = aA.copy();
        div.div(aB);
        return div;
    }
    
    public static vec2 div(vec2 aA, double aB)
    {
        vec2 div = aA.copy();
        div.div(aB);
        return div;
    }
    
    public static vec2 div(double aA, vec2 aB)
    {
        vec2 div = new vec2(aA);
        div.div(aB);
        return div;
    }
    
    public static vec2 div(double aA, double aB)
    {
        double div = aA / aB;
        return new vec2(div);
    }
    
    public static vec2 pow(vec2 aVec, vec2 aExp)
    {
        vec2 pow = aVec.copy();
        pow.pow(aExp);
        return pow;
    }
    
    public static vec2 pow(vec2 aVec, double aExp)
    {
        vec2 pow = aVec.copy();
        pow.pow(aExp);
        return pow;
    }
    
    public static vec2 abs(vec2 aVec)
    {
        vec2 abs = aVec.copy();
        abs.abs();
        return abs;
    }
    
    public static vec2 clamp(vec2 aVec, vec2 aBoundsLo, vec2 aBoundsHi)
    {
        vec2 clamp = aVec.copy();
        clamp.clamp(aBoundsLo, aBoundsHi);
        return clamp;
    }
    
    public static vec2 clamp(vec2 aVec, double aBoundsLo, double aBoundsHi)
    {
        vec2 clamp = aVec.copy();
        clamp.clamp(aBoundsLo, aBoundsHi);
        return clamp;
    }
    
    public static vec2 norm(vec2 aVec)
    {
        vec2 norm = aVec.copy();
        norm.norm();
        return norm;
    }
    
    public static vec2 lerp(vec2 aA, vec2 aB, double aInterpTerm)
    {
        vec2 lerp = aA.copy();
        lerp.lerp(aB, aInterpTerm);
        return lerp;
    }
    
    public static vec2 proj(vec2 aVec, vec2 aBasis)
    {
        vec2 proj = aVec.copy();
        proj.proj(aBasis);
        return proj;
    }
    
    public static vec2 perp(vec2 aVec)
    {
        return new vec2(
            -aVec.y, 
            aVec.x
        );
    }
    
    
    
}
